package no.group.gowther.service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import no.group.gowther.dao.TagDao;
import no.group.gowther.dao.entity.SubTag;
import no.group.gowther.dao.entity.Tag;
import no.group.gowther.exception.BusinessException;
import no.group.gowther.exception.ErrorMessage;
import no.group.gowther.service.dto.ListDTO;
import no.group.gowther.service.dto.TagDTO;
import no.group.gowther.service.dto.param.TagCreateDTO;
import no.group.gowther.service.dto.param.TagQueryDTO;
import no.group.gowther.service.dto.param.TagRenameDTO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * @author ayatsuji
 * @date 2022-05-10 22:40
 * @since
 */
@Service
public class TagServiceImpl extends ServiceImpl<TagDao, Tag> implements TagService {

    @Autowired
    private SubTagService subTagService;

    @Override
    public List<TagDTO> querySubTag(Optional<Long> tagId) {
        List<Tag> tags;
        if (tagId.isPresent()) {
            tags = baseMapper.querySubTag(tagId.get());
        } else {
            tags = list(new LambdaQueryWrapper<Tag>().eq(Tag::getRoot, true).select(Tag::getId, Tag::getName));
        }
        return tags.stream().map(tag -> new TagDTO(tag.getId(), tag.getName(), tag.extractLastName())).collect(Collectors.toList());
    }

    @Override
    public void rename(TagRenameDTO tagRenameDTO) {
        final Tag parentTag = getById(tagRenameDTO.getId());
        final List<Tag> descendentTag = list(new LambdaQueryWrapper<Tag>().likeRight(Tag::getName, parentTag.createSubTagPrefix()));
        final ArrayList<Tag> renameTag = new ArrayList<>(descendentTag);
        renameTag.add(parentTag);

        final String newCompleteTagName = parentTag.generateNewCompleteTagName(tagRenameDTO.getNewTagName());
        for (Tag each : renameTag) {
            final String newTagName = each.getName().replaceFirst(parentTag.getName(), newCompleteTagName);
            each.setName(newTagName);
            each.setUpdatedAt(Timestamp.valueOf(LocalDateTime.now()));
        }
        updateBatchById(renameTag);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delete(Long id) {
        List<Long> deleteTag = new ArrayList<>();
        deleteTag.add(id);
        recursionQuerySubTag(deleteTag, Collections.singletonList(id));
        removeBatchByIds(deleteTag);
    }

    private void recursionQuerySubTag(List<Long> result, List<Long> parentTag) {
        final List<Long> subTag = subTagService.list(new LambdaQueryWrapper<SubTag>().in(SubTag::getTagId, parentTag).select(SubTag::getSubTagId)).stream()
                .map(SubTag::getSubTagId).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(subTag)) {
            return;
        }
        result.addAll(subTag);
        recursionQuerySubTag(result, subTag);
    }

    @Override
    public ListDTO<TagDTO> queryTagFuzzily(TagQueryDTO tagQueryDTO) {
        Page<Tag> tagPage = page(
                new Page<>(tagQueryDTO.getPageNum(), tagQueryDTO.getPageSize()),
                new LambdaQueryWrapper<Tag>()
                        .like(Tag::getName, tagQueryDTO.getTagName())
                        .select(Tag::getId, Tag::getName));
        final List<TagDTO> tags = tagPage.getRecords().stream()
                .map(tag -> new TagDTO(tag.getId(), tag.getName(), tag.extractLastName()))
                .sorted().collect(Collectors.toList());
        return new ListDTO<>(tagPage.getTotal(), tags);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void create(TagCreateDTO tagCreateDTO) {
        Tag newTag;
        Tag parentTag = null;
        if (tagCreateDTO.getParentTagId() == null) {
            newTag = Tag.createRootTag(tagCreateDTO.getTagName());
        } else {
            parentTag = getById(tagCreateDTO.getParentTagId());
            if (parentTag == null) {
                throw BusinessException.createException(ErrorMessage.PARAM_ERROR.getErrorCode(), "parentTag=[%s]不存在".formatted(tagCreateDTO.getParentTagId()));
            }
            newTag = Tag.createSubTag(parentTag.getName(), tagCreateDTO.getTagName());
        }
        checkSameNameTag(newTag.getName());
        save(newTag);
        if (parentTag != null) {
            final SubTag subTag = new SubTag(parentTag.getId(), newTag.getId());
            subTagService.save(subTag);
        }
    }

    private void checkSameNameTag(String tagName) {
        long count = count(Wrappers.<Tag>lambdaQuery().eq(Tag::getName, tagName));
        if (count != 0) {
            throw BusinessException.createException(ErrorMessage.PARAM_ERROR.getErrorCode(), "已存在同名标签");
        }
    }

    @Override
    public List<Long> queryDescendantTag(List<Long> id) {
        List<Long> descendantTag = new ArrayList<>(id);
        recursionQuerySubTag(descendantTag, id);
        return descendantTag;
    }

    @Override
    public TagDTO queryTab(Long id) {
        Tag tag = getById(id);
        return new TagDTO(tag.getId(), tag.getName(), tag.extractLastName());
    }
}
